﻿// <copyright file="EventFactory.cs" company="Paxoft">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Pavel Kotlyar</author>
// <email>paxer@mail.ru</email>
// <date>2009-10-12</date>
// <summary>This class is a part of open source .Net Factory Framework for Web Developers. Make Web Dev easy and fun and reduce boring things is the main aim of this project. Project url http://factoryframework.codeplex.com/ . Licence GNU Library General Public License (LGPL)</summary>

namespace Factory.Framework
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Web;
    using System.Xml;    
    using System.Xml.Linq;
    using System.Xml.XPath;

    #region Public Enums
    /// <summary>
    /// List of events types
    /// </summary>
    public enum EventType
    {
        /// <summary>
        /// Information event type
        /// </summary>
        Information,

        /// <summary>
        /// Warning evemt type
        /// </summary>
        Warning,

        /// <summary>
        /// Error event type
        /// </summary>
        Error
    }

    /// <summary>
    /// List if default colors for events
    /// </summary>
    public enum EventColor
    {
        /// <summary>
        /// Red event color, could be used while render report
        /// </summary>
        Red,

        /// <summary>
        /// Yello event color, could be used while render report
        /// </summary>        
        Yellow,

        /// <summary>
        /// White event color, could be used while render report
        /// </summary>
        White
    }

    /// <summary>
    /// Event notification type
    /// </summary>
    public enum EventNotificationType
    {
        /// <summary>
        /// Notify about event by email
        /// </summary>
        Email,

        /// <summary>
        /// Notify about event by saving it 
        /// </summary>
        Save,

        /// <summary>
        /// Notify about event by saving it and sending notification email
        /// </summary>
        EmailAndSave
    }
    #endregion

    /// <summary>
    /// Event factory. Class provide functionality for logging application events like Error, Warning, Information etc
    /// </summary>    
    public class EventFactory : Factory
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the EventFactory class.
        /// </summary>
        /// <param name="eventFactoryConfigPath">EventFactory configuration path</param>
        /// <param name="emailFactoryConfigPath">EmailFactory configuration path</param>
        public EventFactory(string eventFactoryConfigPath, string emailFactoryConfigPath)
        {
            this.EventFactoryConfigPath = eventFactoryConfigPath;
            this.EmailFactoryConfigPath = emailFactoryConfigPath;
        }

        /// <summary>
        /// Initializes a new instance of the EventFactory class.
        /// </summary>
        /// <param name="eventFactoryConfigPath">EventFactory configuration path</param>
        /// <param name="emailFactoryConfigPath">EmailFactory configuration path</param>
        /// <param name="eventType">New event type</param>
        /// <param name="eventDescription">New event description</param>
        /// <param name="eventNotificationType">New event notification type</param>
        public EventFactory(string eventFactoryConfigPath, string emailFactoryConfigPath, EventType eventType, string eventDescription, EventNotificationType eventNotificationType)
        {
            if (this.FactoryEnabled)
            {
                this.EventFactoryConfigPath = eventFactoryConfigPath;
                this.EmailFactoryConfigPath = emailFactoryConfigPath;
                this.AddEvent(eventType, eventDescription, eventNotificationType);
            }
        }

        /// <summary>
        /// Initializes a new instance of the EventFactory class.
        /// </summary>
        /// <param name="eventFactoryConfigPath">EventFactory configuration path</param>
        /// <param name="emailFactoryConfigPath">EmailFactory configuration path</param>
        /// <param name="eventType">New evemt type</param>
        /// <param name="eventColor">New event color</param>
        /// <param name="eventDescription">New event description</param>
        /// <param name="eventNotificationType">New event notification type</param>
        /// <param name="additionalInfo">New event additional information</param>
        public EventFactory(string eventFactoryConfigPath, string emailFactoryConfigPath, EventType eventType, EventColor eventColor, string eventDescription, EventNotificationType eventNotificationType, string additionalInfo)
        {
            if (this.FactoryEnabled)
            {
                this.EventFactoryConfigPath = eventFactoryConfigPath;
                this.EmailFactoryConfigPath = emailFactoryConfigPath;
                this.AddEvent(eventType, eventColor, eventDescription, eventNotificationType, additionalInfo);
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets Event Factory config file path
        /// </summary>
        public string EventFactoryConfigPath { get; set; }

        /// <summary>
        /// Gets or sets Email Factory config file path
        /// </summary>
        public string EmailFactoryConfigPath { get; set; }

        /// <summary>
        /// Gets or sets path where evet log stored
        /// </summary>
        public string EventLogPath { get; set; }

        /// <summary>
        /// Gets or sets default From email address
        /// </summary>
        public string[] DefaultEmailToAddress { get; set; }

        /// <summary>
        /// Gets or sets event log type
        /// </summary>
        public string EventLogType { get; set; }

        /// <summary>
        /// Gets or sets event max log size
        /// </summary>
        public int EventLogSize { get; set; }

        #endregion

        #region Private Properties
        /// <summary>
        /// Gets Event Log Path for current mode
        /// </summary>
        private string GetEventLogPath
        {
            get
            {
                return HttpContext.Current.Server.MapPath(this.EventLogPath);
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Add new event
        /// </summary>
        /// <param name="eventType">Event Type</param>
        /// <param name="eventDescription">Event description</param>
        /// <param name="eventNotificationType">Event notification type</param>
        public void AddEvent(EventType eventType, string eventDescription, EventNotificationType eventNotificationType)
        {
            if (this.FactoryEnabled)
            {
                // Load Event Factory Settings
                this.LoadEventFactorySettings();

                // Set Default Colors for Events
                EventColor eventColor = EventColor.White;
                switch (eventType)
                {
                    case EventType.Error:
                        eventColor = EventColor.Red;
                        break;
                    case EventType.Warning:
                        eventColor = EventColor.Yellow;
                        break;
                    case EventType.Information:
                        eventColor = EventColor.White;
                        break;
                }

                this.PrepareToSave(eventType, eventColor, eventDescription, eventNotificationType, null);
            }
        }

        /// <summary>
        /// Add new event
        /// </summary>
        /// <param name="eventType">Event type</param>
        /// <param name="eventColor">Event color</param>
        /// <param name="eventDescription">Event description</param>
        /// <param name="eventNotificationType">Event notification Type</param>
        /// <param name="additionalInfo">Event additional info</param>
        public void AddEvent(EventType eventType, EventColor eventColor, string eventDescription, EventNotificationType eventNotificationType, string additionalInfo)
        {
            if (this.FactoryEnabled)
            {
                this.PrepareToSave(eventType, eventColor, eventDescription, eventNotificationType, additionalInfo);
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Execute methods depending from event notification type
        /// </summary>
        /// <param name="eventType">Event type</param>
        /// <param name="eventColor">Event color</param>
        /// <param name="eventDescription">Event description</param>
        /// <param name="eventNotificationType">Event notification type</param>
        /// <param name="additionalInfo">Event additional info</param>
        private void PrepareToSave(EventType eventType, EventColor eventColor, string eventDescription, EventNotificationType eventNotificationType, string additionalInfo)
        {
            if (additionalInfo == null)
            {
                additionalInfo = string.Empty;
            }

            string url = HttpContext.Current.Request.Url.ToString();
            string emailText = DateTime.Now.ToString() + System.Environment.NewLine + url + System.Environment.NewLine + eventDescription + System.Environment.NewLine + additionalInfo;

            // Depending from Notification Type Send Email, or Save etc
            switch (eventNotificationType)
            {
                case EventNotificationType.Email:
                    this.NotifyByEmail(this.EmailFactoryConfigPath, this.DefaultEmailToAddress, eventType.ToString(), emailText);
                    break;
                case EventNotificationType.EmailAndSave:
                    this.NotifyByEmail(this.EmailFactoryConfigPath, this.DefaultEmailToAddress, eventType.ToString(), emailText);
                    this.SaveEvent(eventType, eventColor, eventDescription, additionalInfo, url);
                    break;
                case EventNotificationType.Save:
                    this.SaveEvent(eventType, eventColor, eventDescription, additionalInfo, url);
                    break;
            }
        }

        /// <summary>
        /// General Save Event method
        /// </summary>
        /// <param name="eventType">Event type</param>
        /// <param name="eventColor">Event color</param>
        /// <param name="eventDescription">Event description</param>
        /// <param name="additionalInfo">Event additional info</param>
        /// <param name="url">Url where the event occurred</param>
        private void SaveEvent(EventType eventType, EventColor eventColor, string eventDescription, string additionalInfo, string url)
        {
            // Save event depending from LogType (XML,TEXT, etc)
            switch (this.EventLogType)
            {
                case "text":
                    this.SaveEventInText(eventType, eventColor, eventDescription, additionalInfo, url);
                    break;

                case "xml":
                    this.SaveEventInXml(eventType, eventColor, eventDescription, additionalInfo, url);
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// Save event in text file
        /// </summary>
        /// <param name="eventType">Event type</param>
        /// <param name="eventColor">Event color</param>
        /// <param name="eventDescription">Event description</param>
        /// <param name="additionalInfo">Event additional information</param>
        /// <param name="url">Url where the event occurred</param>
        private void SaveEventInText(EventType eventType, EventColor eventColor, string eventDescription, string additionalInfo, string url)
        {
            string eventText = String.Format("\n {0} \n {1} \n {2} \n {3} \n {4}", DateTime.Now.ToString(), url, eventType.ToString(), eventColor.ToString(), eventDescription, additionalInfo);
            string eventLogPath = this.GetEventLogPath;

            // Write Event in text on HDD
            if (File.Exists(eventLogPath))
            {
                // Getting Log Size
                FileInfo fileInfo = new FileInfo(eventLogPath);
                int cuurentEventLogSize = (int)fileInfo.Length;

                // Delete it if it have size more than allowed for current log mode
                if (this.EventLogSize != 0)
                {
                    if (cuurentEventLogSize >= this.EventLogSize)
                    {
                        File.Delete(this.GetEventLogPath);
                    }
                }

                StreamWriter streamWriter;
                streamWriter = File.AppendText(eventLogPath);
                streamWriter.WriteLine(eventText);
                streamWriter.Close();
            }
            else
            {
                // create new one
                TextWriter textWriter = new StreamWriter(eventLogPath);
                textWriter.WriteLine(eventText);
                textWriter.Close();
            }
        }

        /// <summary>
        /// Save event in XML file
        /// </summary>
        /// <param name="eventType">Event type</param>
        /// <param name="eventColor">Event color</param>
        /// <param name="eventDescription">Event description</param>
        /// <param name="additionalInfo">Event additional info</param>
        /// <param name="url">Url where the event occurred</param>
        private void SaveEventInXml(EventType eventType, EventColor eventColor, string eventDescription, string additionalInfo, string url)
        {
            string eventLogPath = this.GetEventLogPath;

            // Getting Log Size
            FileInfo fileInfo = new FileInfo(eventLogPath);
            int cuurentEventLogSize = (int)fileInfo.Length;

            // Delete it if it have size more than allowed for current log mode
            if (this.EventLogSize != 0)
            {
                if (cuurentEventLogSize >= this.EventLogSize)
                {
                    File.Delete(this.GetEventLogPath);
                }
            }

            // Write Event in XML on HDD
            if (File.Exists(eventLogPath))
            {
                XElement eventLogXml = XElement.Load(HttpContext.Current.Server.MapPath(this.EventLogPath));

                eventLogXml.Add(new XElement("event",
                                            new XElement("datetime", DateTime.Now.ToString()),
                                            new XElement("type", eventType.ToString()),
                                            new XElement("color", eventColor),
                                            new XElement("url", url),
                                            new XElement("description", eventDescription),
                                            new XElement("additionalInfo", additionalInfo)
                                            )
                                 );

                eventLogXml.Save(eventLogPath);
            }
            else
            {
                XDocument doc = new XDocument(
                        new XDeclaration("1.0", "utf-8", "yes"),
                          new XElement("log",
                              new XElement("event",
                                  new XElement("datetime", DateTime.Now.ToString()),
                                  new XElement("type", eventType.ToString()),
                                  new XElement("color", eventColor),
                                  new XElement("url", url),
                                  new XElement("description", eventDescription),
                                  new XElement("additionalInfo", additionalInfo)
                              )
                          )
                      );

                doc.Save(eventLogPath);
            }
        }

        /// <summary>
        /// Send event notification to email
        /// </summary>
        /// <param name="emailFactoryConfigFilePath">EmailFactory configuration file path</param>
        /// <param name="listOfToAddress">String array of email address</param>
        /// <param name="emailSubject">Email subject</param>
        /// <param name="emailText">Email body</param>
        private void NotifyByEmail(string emailFactoryConfigFilePath, string[] listOfToAddress, string emailSubject, string emailText)
        {
            var emailFactory = new EmailFactory(emailFactoryConfigFilePath);
            emailFactory.IsHtml = false;
            emailFactory.SendMail(listOfToAddress, emailSubject, emailText);
        }

        #endregion

        #region Initialization Section

        /// <summary>
        /// Load Event Factory Settings from config file
        /// </summary>
        private void LoadEventFactorySettings()
        {
            var eventLogModeSettings = new Dictionary<string, string>();
            var doc = new XPathDocument(HttpContext.Current.Server.MapPath(this.EventFactoryConfigPath));
            XPathNavigator navigator = doc.CreateNavigator();

            // Get Default TO Email Address
            string defaultEmailToAddress = navigator.SelectSingleNode("/eventFactorySettings/@defaultEmailToAddress").Value;
            this.DefaultEmailToAddress = new string[] { defaultEmailToAddress };

            // Get Event Loging mode (test, production etc)
            string eventLogMode = navigator.SelectSingleNode("/eventFactorySettings/@mode").Value;
            XPathNodeIterator iterator = navigator.Select("/eventFactorySettings/mode/" + eventLogMode + "/*");

            while (iterator.MoveNext())
            {
                eventLogModeSettings.Add(iterator.Current.Name, iterator.Current.Value);
            }

            // log file path
            this.EventLogPath = eventLogModeSettings["eventLogPath"];

            // xml writing or txt
            this.EventLogType = eventLogModeSettings["eveltLogType"];

            // max allowed log size if 0 then unlimited
            this.EventLogSize = Convert.ToInt32(eventLogModeSettings["eventLogSize"]);
        }
        #endregion
    }
}
